baubs git
Commit ed3950b5d031b20b51ffaf8863537e471f7747e9
Parents : bd80e95
Author : Jan-Henrik Bruhn <jan-henrik.bruhn@offis.de>
Date : 2025-12-20T19:38:27+01:00
feature: Migrate FileUpload to shadcn/ui and fix dark mode
Migrated FileUpload component to use shadcn/ui Card, Button, Alert, and Progress components. Updated dark mode CSS variables to use media query approach for automatic system theme detection. Fixed Card component padding and button styling for better visual consistency.
Changes:
- Replaced custom div with shadcn Card and CardContent components
- Migrated buttons to shadcn Button component with outline variant for Choose File
- Replaced custom alerts with shadcn Alert components
- Replaced custom progress bars with shadcn Progress component
- Fixed Card padding by adding p-0 to Card and rounded-lg to CardContent
- Changed dark mode from .dark class to @media (prefers-color-scheme: dark)
- Fixed primary-foreground color in dark mode for proper white text contrast
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes
3 files changed, 125 insertions(+), 132 deletions(-)
Diff
diff --git a/src/App.css b/src/App.css
index 7f17468..8f860de 100644
--- a/src/App.css
+++ b/src/App.css
@@ -60,43 +60,45 @@
--radius: 0.5rem;
}
- .dark {
- --background: 222.2 84% 4.9%;
- --foreground: 210 40% 98%;
+ @media (prefers-color-scheme: dark) {
+ :root {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
- --card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
- --popover: 222.2 84% 4.9%;
- --popover-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
- --primary: 217.2 91.2% 59.8%; /* blue-500 lighter for dark */
- --primary-foreground: 222.2 47.4% 11.2%;
+ --primary: 217.2 91.2% 59.8%; /* blue-500 lighter for dark */
+ --primary-foreground: 210 40% 98%;
- --secondary: 20.5 90.2% 48.2%; /* orange-600 for dark */
- --secondary-foreground: 210 40% 98%;
+ --secondary: 20.5 90.2% 48.2%; /* orange-600 for dark */
+ --secondary-foreground: 210 40% 98%;
- --muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
- --accent: 263.4 70% 50.4%; /* purple-600 for dark */
- --accent-foreground: 210 40% 98%;
+ --accent: 263.4 70% 50.4%; /* purple-600 for dark */
+ --accent-foreground: 210 40% 98%;
- --destructive: 0 62.8% 30.6%; /* red-900 */
- --destructive-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%; /* red-900 */
+ --destructive-foreground: 210 40% 98%;
- --success: 142.1 70.6% 45.3%; /* green-500 for dark */
- --success-foreground: 210 40% 98%;
+ --success: 142.1 70.6% 45.3%; /* green-500 for dark */
+ --success-foreground: 210 40% 98%;
- --warning: 47.9 95.8% 53.1%; /* amber-400 for dark */
- --warning-foreground: 26 83.3% 14.1%;
+ --warning: 47.9 95.8% 53.1%; /* amber-400 for dark */
+ --warning-foreground: 26 83.3% 14.1%;
- --info: 188.7 85.7% 53.3%; /* cyan-500 */
- --info-foreground: 210 40% 98%;
+ --info: 188.7 85.7% 53.3%; /* cyan-500 */
+ --info-foreground: 210 40% 98%;
- --border: 217.2 32.6% 17.5%;
- --input: 217.2 32.6% 17.5%;
- --ring: 224.3 76.3% 48%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 224.3 76.3% 48%;
+ }
}
}
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx
index 30ebfc3..15309ca 100644
--- a/src/components/FileUpload.tsx
+++ b/src/components/FileUpload.tsx
@@ -21,6 +21,12 @@ import {
} from "@heroicons/react/24/solid";
import { createFileService } from "../platform";
import type { IFileService } from "../platform/interfaces/IFileService";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent } from "@/components/ui/card";
+import { Alert, AlertDescription } from "@/components/ui/alert";
+import { Progress } from "@/components/ui/progress";
+import { Loader2 } from "lucide-react";
+import { cn } from "@/lib/utils";
export function FileUpload() {
// Machine store
@@ -202,31 +208,30 @@ export function FileUpload() {
: "text-gray-600 dark:text-gray-400";
return (
- <div
- className={`bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md border-l-4 ${borderColor}`}
- >
- <div className="flex items-start gap-3 mb-3">
- <DocumentTextIcon
- className={`w-6 h-6 ${iconColor} flex-shrink-0 mt-0.5`}
- />
- <div className="flex-1 min-w-0">
- <h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-1">
- Pattern File
- </h3>
- {pesData && displayFileName ? (
- <p
- className="text-xs text-gray-600 dark:text-gray-400 truncate"
- title={displayFileName}
- >
- {displayFileName}
- </p>
- ) : (
- <p className="text-xs text-gray-600 dark:text-gray-400">
- No pattern loaded
- </p>
- )}
+ <Card className={cn("p-0 border-l-4", borderColor)}>
+ <CardContent className="p-4 rounded-lg">
+ <div className="flex items-start gap-3 mb-3">
+ <DocumentTextIcon
+ className={cn("w-6 h-6 flex-shrink-0 mt-0.5", iconColor)}
+ />
+ <div className="flex-1 min-w-0">
+ <h3 className="text-sm font-semibold text-gray-900 dark:text-white mb-1">
+ Pattern File
+ </h3>
+ {pesData && displayFileName ? (
+ <p
+ className="text-xs text-gray-600 dark:text-gray-400 truncate"
+ title={displayFileName}
+ >
+ {displayFileName}
+ </p>
+ ) : (
+ <p className="text-xs text-gray-600 dark:text-gray-400">
+ No pattern loaded
+ </p>
+ )}
+ </div>
</div>
- </div>
{resumeAvailable && resumeFileName && (
<div className="bg-success-50 dark:bg-success-900/20 border border-success-200 dark:border-success-800 px-3 py-2 rounded mb-3">
@@ -253,63 +258,66 @@ export function FileUpload() {
className="hidden"
disabled={isLoading || patternUploaded || isUploading}
/>
- <label
- htmlFor={fileService.hasNativeDialogs() ? undefined : "file-input"}
+ <Button
+ asChild={!fileService.hasNativeDialogs()}
onClick={
fileService.hasNativeDialogs()
? () => handleFileChange()
: undefined
}
- className={`flex-[2] flex items-center justify-center gap-2 px-3 py-2.5 sm:py-2 rounded font-semibold text-sm transition-all ${
- isLoading || patternUploaded || isUploading
- ? "opacity-50 cursor-not-allowed bg-gray-400 dark:bg-gray-600 text-white"
- : "cursor-pointer bg-gray-600 dark:bg-gray-700 text-white hover:bg-gray-700 dark:hover:bg-gray-600"
- }`}
+ disabled={isLoading || patternUploaded || isUploading}
+ variant="outline"
+ className="flex-[2]"
>
- {isLoading ? (
+ {fileService.hasNativeDialogs() ? (
<>
- <svg
- className="w-3.5 h-3.5 animate-spin"
- fill="none"
- viewBox="0 0 24 24"
- >
- <circle
- className="opacity-25"
- cx="12"
- cy="12"
- r="10"
- stroke="currentColor"
- strokeWidth="4"
- ></circle>
- <path
- className="opacity-75"
- fill="currentColor"
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
- ></path>
- </svg>
- <span>Loading...</span>
- </>
- ) : patternUploaded ? (
- <>
- <CheckCircleIcon className="w-3.5 h-3.5" />
- <span>Locked</span>
+ {isLoading ? (
+ <>
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
+ <span>Loading...</span>
+ </>
+ ) : patternUploaded ? (
+ <>
+ <CheckCircleIcon className="w-3.5 h-3.5" />
+ <span>Locked</span>
+ </>
+ ) : (
+ <>
+ <FolderOpenIcon className="w-3.5 h-3.5" />
+ <span>Choose PES File</span>
+ </>
+ )}
</>
) : (
- <>
- <FolderOpenIcon className="w-3.5 h-3.5" />
- <span>Choose PES File</span>
- </>
+ <label htmlFor="file-input" className="flex items-center gap-2">
+ {isLoading ? (
+ <>
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
+ <span>Loading...</span>
+ </>
+ ) : patternUploaded ? (
+ <>
+ <CheckCircleIcon className="w-3.5 h-3.5" />
+ <span>Locked</span>
+ </>
+ ) : (
+ <>
+ <FolderOpenIcon className="w-3.5 h-3.5" />
+ <span>Choose PES File</span>
+ </>
+ )}
+ </label>
)}
- </label>
+ </Button>
{pesData &&
canUploadPattern(machineStatus) &&
!patternUploaded &&
uploadProgress < 100 && (
- <button
+ <Button
onClick={handleUpload}
disabled={!isConnected || isUploading || !boundsCheck.fits}
- className="flex-1 px-3 py-2.5 sm:py-2 bg-primary-600 dark:bg-primary-700 text-white rounded font-semibold text-sm hover:bg-primary-700 dark:hover:bg-primary-600 active:bg-primary-800 dark:active:bg-primary-500 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
+ className="flex-1"
aria-label={
isUploading
? `Uploading pattern: ${uploadProgress.toFixed(0)}% complete`
@@ -318,36 +326,18 @@ export function FileUpload() {
>
{isUploading ? (
<>
- <svg
- className="w-3.5 h-3.5 animate-spin inline mr-1"
- fill="none"
- viewBox="0 0 24 24"
- >
- <circle
- className="opacity-25"
- cx="12"
- cy="12"
- r="10"
- stroke="currentColor"
- strokeWidth="4"
- ></circle>
- <path
- className="opacity-75"
- fill="currentColor"
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
- ></path>
- </svg>
+ <Loader2 className="w-3.5 h-3.5 animate-spin" />
{uploadProgress > 0
? uploadProgress.toFixed(0) + "%"
: "Uploading"}
</>
) : (
<>
- <ArrowUpTrayIcon className="w-3.5 h-3.5 inline mr-1" />
+ <ArrowUpTrayIcon className="w-3.5 h-3.5" />
Upload
</>
)}
- </button>
+ </Button>
)}
</div>
@@ -364,12 +354,7 @@ export function FileUpload() {
{pyodideProgress.toFixed(0)}%
</span>
</div>
- <div className="h-2.5 bg-gray-300 dark:bg-gray-600 rounded-full overflow-hidden shadow-inner relative">
- <div
- className="h-full bg-gradient-to-r from-primary-500 via-primary-600 to-primary-700 dark:from-primary-600 dark:via-primary-700 dark:to-primary-800 transition-all duration-300 ease-out relative overflow-hidden after:absolute after:inset-0 after:bg-gradient-to-r after:from-transparent after:via-white/30 after:to-transparent after:animate-[shimmer_2s_infinite] rounded-full"
- style={{ width: `${pyodideProgress}%` }}
- />
- </div>
+ <Progress value={pyodideProgress} className="h-2.5" />
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1.5 italic">
{isLoading && !pyodideReady
? "File dialog will open automatically when ready"
@@ -393,15 +378,22 @@ export function FileUpload() {
}}
>
{pesData && !canUploadPattern(machineStatus) && (
- <div className="bg-warning-100 dark:bg-warning-900/20 text-warning-800 dark:text-warning-200 px-3 py-2 rounded border border-warning-200 dark:border-warning-800 text-sm">
- Cannot upload while {getMachineStateCategory(machineStatus)}
- </div>
+ <Alert className="bg-warning-100 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800">
+ <AlertDescription className="text-warning-800 dark:text-warning-200 text-sm">
+ Cannot upload while {getMachineStateCategory(machineStatus)}
+ </AlertDescription>
+ </Alert>
)}
{pesData && boundsCheck.error && (
- <div className="bg-danger-100 dark:bg-danger-900/20 text-danger-800 dark:text-danger-200 px-3 py-2 rounded border border-danger-200 dark:border-danger-800 text-sm">
- <strong>Pattern too large:</strong> {boundsCheck.error}
- </div>
+ <Alert
+ variant="destructive"
+ className="bg-danger-100 dark:bg-danger-900/20 border-danger-200 dark:border-danger-800"
+ >
+ <AlertDescription className="text-danger-800 dark:text-danger-200 text-sm">
+ <strong>Pattern too large:</strong> {boundsCheck.error}
+ </AlertDescription>
+ </Alert>
)}
</div>
@@ -417,14 +409,13 @@ export function FileUpload() {
: "Starting..."}
</span>
</div>
- <div className="h-2.5 bg-gray-300 dark:bg-gray-600 rounded-full overflow-hidden shadow-inner relative">
- <div
- className="h-full bg-gradient-to-r from-secondary-500 via-secondary-600 to-secondary-700 dark:from-secondary-600 dark:via-secondary-700 dark:to-secondary-800 transition-all duration-300 ease-out relative overflow-hidden after:absolute after:inset-0 after:bg-gradient-to-r after:from-transparent after:via-white/30 after:to-transparent after:animate-[shimmer_2s_infinite] rounded-full"
- style={{ width: `${uploadProgress}%` }}
- />
- </div>
+ <Progress
+ value={uploadProgress}
+ className="h-2.5 [&>div]:bg-gradient-to-r [&>div]:from-secondary-500 [&>div]:via-secondary-600 [&>div]:to-secondary-700 dark:[&>div]:from-secondary-600 dark:[&>div]:via-secondary-700 dark:[&>div]:to-secondary-800"
+ />
</div>
)}
- </div>
+ </CardContent>
+ </Card>
);
}
diff --git a/src/components/PatternSummaryCard.tsx b/src/components/PatternSummaryCard.tsx
index d80773f..354c66e 100644
--- a/src/components/PatternSummaryCard.tsx
+++ b/src/components/PatternSummaryCard.tsx
@@ -30,8 +30,8 @@ export function PatternSummaryCard() {
const canDelete = canDeletePattern(machineStatus);
return (
- <Card className="border-l-4 border-primary-600 dark:border-primary-500">
- <CardContent className="p-4">
+ <Card className="p-0 border-l-4 border-primary-600 dark:border-primary-500">
+ <CardContent className="p-4 rounded-lg">
<div className="flex items-start gap-3 mb-3">
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5" />
<div className="flex-1 min-w-0">
Served by rngit 1.3.3 - Generated in 0.07s